<?php
// @formatter:off
/**
 * @file Class.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-08-20 00:23:06 +0000 (Tue, 20 Aug 2013) $
 *
 * $Id: Class.php 102 2013-08-20 00:23:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0.0.1/extensions/TagsRelator/Class.php $
 */

/**
 * Adding all extension's hooks
 * @{
 */
define("WC_E_TAGS_RELATOR_VERSION",	"1.0");
define("WC_E_TAGS_RELATOR_ROOTDIR",	dirname(__FILE__));
define("WC_E_TAGS_RELATOR_NAME",	basename(WC_E_TAGS_RELATOR_ROOTDIR));
define("WC_E_TAGS_RELATOR_URI",		dirname($_SERVER["SCRIPT_NAME"])."/{$wcPaths["extensions-directory-uri"]}/".WC_E_TAGS_RELATOR_NAME);
define("WC_E_TAGS_RELATOR_CONTROLERS",	dirname(__FILE__)."/controlers");
define("WC_E_TAGS_RELATOR_TEMPLATES",	dirname(__FILE__)."/templates");

$extensionBasicInfo = array(
	"author"	=> "Alejandro Dario Simi",
	"version"	=> WC_E_TAGS_RELATOR_VERSION,
	"url"		=> array(
		"title"		=> "wcomix:Extension:TagsRelator",
		"href"		=> "http://wiki.daemonraco.com/wiki/Wcomix:Extension:TagsRelator"
	)
);
$wcCanvasActions["tare.edit"] = array_merge($extensionBasicInfo, array(
	"name"		=> "Tags Relator: Action Edit",
	"description"	=> "Tags Relator extension: Action Editor",
	"manager"	=> WC_E_TAGS_RELATOR_CONTROLERS."/c.edit.php",
	"template"	=> WC_E_TAGS_RELATOR_TEMPLATES."/c.edit.html",
	"pre-js"	=> WC_E_TAGS_RELATOR_TEMPLATES."/c.tare.js"
));
$wcCanvasActions["tare.create"] = array_merge($extensionBasicInfo, array(
	"name"		=> "Tags Relator: Action Create",
	"description"	=> "Tags Relator extension: Action Creator",
	"manager"	=> WC_E_TAGS_RELATOR_CONTROLERS."/c.create.php",
	"template"	=> WC_E_TAGS_RELATOR_TEMPLATES."/c.create.html",
	"pre-js"	=> WC_E_TAGS_RELATOR_TEMPLATES."/c.tare.js"
));
$wcCanvasActions["tare.remove"] = array_merge($extensionBasicInfo, array(
	"name"		=> "Tags Relator: Action Remove",
	"description"	=> "Tags Relator extension: Action Remover",
	"manager"	=> WC_E_TAGS_RELATOR_CONTROLERS."/c.remove.php",
	"template"	=> WC_E_TAGS_RELATOR_TEMPLATES."/c.remove.html",
	"pre-js"	=> WC_E_TAGS_RELATOR_TEMPLATES."/c.tare.js"
));

$auxURI = $wcPaths['extensions-directory-uri'].substr(dirname(__FILE__), strlen($wcPaths['extensions-directory']));
$auxDIR = substr(dirname(__FILE__), strlen($wcROOTDIR));
$wcDatabase["tables"][]		= "{$auxDIR}/db/wc_acts_tare.tab.xml";
$wcDatabase["indexes"][]	= "{$auxDIR}/db/wc_acts_tare.pk.xml";
// @formatter:on
/* @} */

/**
 * @class TagsRelator
 * @abstract
 * @author Alejandro Dario Simi
 *
 */
class TagsRelator extends WCRuleAction {
	public static $TYPE = "TARE";

	public static $ModeAlso = "ALSO";
	public static $ModeIf = "IF";
	public static $ModePool = "POOL";

	protected $_tags = array();
	protected $_also = array();
	protected $_seekerStmt = false;

	//
	// Public methods.
	public function reload() {
		$this->_tags = array();
		$this->_also = array();

		parent::reload();
	}
	public function run() {
		$out = array();

		$stmtId = __CLASS__."::".__FUNCTION__."[seeker]";
		if($this->_db->has($stmtId)) {
			$this->_seekerStmt = $this->_db->get($stmtId);
		} else {
			$query = "select  mem_comic as id\n";
			$query .= "from    {$this->_dbprefix}tag_members\n";
			$query .= "where   mem_group = :group\n";
			$query .= " and    mem_tag   = :tag_search\n";
			$query .= " and    mem_comic not in (\n";
			$query .= "                select  mem_comic\n";
			$query .= "                from    {$this->_dbprefix}tag_members\n";
			$query .= "                where   mem_group = :group\n";
			$query .= "                 and    mem_tag   = :tag_toadd\n";
			$query .= "        )\n";
			$this->_seekerStmt = $this->_db->prepare($stmtId, $query);
		}

		switch($this->mode) {
			case self::$ModeAlso:
				$out = $this->runModeAlso();
				break;
			case self::$ModeIf:
				$out = $this->runModeIf();
				break;
			case self::$ModePool:
				$out = $this->runModePool();
				break;
		}

		return $out;
	}
	public function tags() {
		return $this->_tags;
	}
	public function tagsAlso() {
		return $this->_also;
	}
	public function tagFrom() {
		return $this->_tagFrom;
	}
	public function tagFromName() {
		return $this->_tagFromName;
	}
	public function tagTo() {
		return $this->_tagTo;
	}
	public function tagToName() {
		return $this->_tagToName;
	}
	public function words() {
		$out = array();

		if($this->target) {
			$out = array_merge($out, WCSearchFactory::StringToPatterns($this->target));
		}
		foreach($this->_tags as $tag) {
			$out = array_merge($out, WCSearchFactory::StringToPatterns($tag->name));
		}
		foreach($this->_also as $tag) {
			$out = array_merge($out, WCSearchFactory::StringToPatterns($tag->name));
		}

		return $out;
	}
	//
	// Protected methods.
	protected function checkTarget() {
		global $wcTagsHolder;

		if($this->target) {
			$tag = $wcTagsHolder->itemByName($this->target, $this->_group);
			if(!$tag) {
				$wcTagsHolder->create($this->target, $this->_group);
			}
		}
	}
	protected function enforceMode() {
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "update  {$this->_dbprefix}acts_tare\n";
			$query .= "set     act_mode = :mode\n";
			$query .= "where   act_rule   = :rule\n";
			$query .= " and    act_group  = :group\n";
			$query .= " and    act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id,
			":mode" => $this->mode
		);
		$stmt->execute($params);
	}
	protected function load() {
		if($this->_type != self::$TYPE) {
			trigger_error("Loaded type and class type are different.", E_USER_ERROR);
		}

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[1]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}acts_tare\n";
			$query .= "where   act_rule   = :rule\n";
			$query .= " and    act_group  = :group\n";
			$query .= " and    act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			global $wcTagsHolder;

			foreach($stmt->fetchAll() as $row) {
				if($row["act_tag"] && !isset($this->_tags[$row["act_tag"]])) {
					$tag = $wcTagsHolder->item($row["act_tag"], $this->_group);
					if($tag && $tag->ok()) {
						$this->_tags[$tag->id] = $tag;
					}
				}

				if($row["act_also"] && !isset($this->_also[$row["act_also"]])) {
					$tag = $wcTagsHolder->item($row["act_also"], $this->_group);
					if($tag && $tag->ok()) {
						$this->_also[$tag->id] = $tag;
					}
				}
			}

			if($row["act_mode"] != $this->mode) {
				$this->enforceMode();
			}
		}

		$this->_ok = true;

		if(!$this->mode) {
			$this->mode = self::$ModeAlso;
		}
		if(count($this->_tags) && ($this->mode == self::$ModeAlso || $this->mode == self::$ModeIf)) {
			$tag = array_values($this->_tags);
			$tag = array_shift($tag);
			$this->target = $tag->name;
		}
	}
	protected function removeSpecific() {
		$out = true;

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}acts_tare\n";
			$query .= "where       act_rule   = :rule\n";
			$query .= " and        act_group  = :group\n";
			$query .= " and        act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id
		);

		if(!$stmt->execute($params)) {
			$out = false;
		}

		return $out;
	}
	protected function runModeAlso() {
		$out = array();

		if($this->_ok) {
			$idMembers = array();

			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				global $wcTagsHolder;
				global $wcComicsHolder;

				$tag = $wcTagsHolder->itemByName($this->target, $this->_group);

				if($tag && $tag->ok()) {
					foreach($this->_also as $toAdd) {
						//
						// Searching all tags.
						$comicIds = $this->seeker($tag, $toAdd);
						foreach($comicIds as $id) {
							$comic = $wcComicsHolder->item($id, $this->_group);
							if($comic && $comic->ok()) {
								if(!isset($out["items"])) {
									$out["items"] = array();
								}
								//
								// It seems not
								// to be present,
								// therefore it
								// is added.
								$aux = array(
									"action" => "Adding '{$comic->name}' ({$comic->id}@{$comic->directory}) to tag '{$toAdd->name}' ({$toAdd->id})",
									"member" => $comic,
									"status" => true,
									"fatal" => true
								);
								$toAdd->add($comic);
								$out["items"][] = $aux;
							}
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeIf() {
		$out = array();

		if($this->_ok) {
			$idMembers = array();

			$tagsCount = count($this->_tags);
			if($tagsCount > 1) {
				$this->setConflict(true);
				trigger_error(__CLASS__."::".__FUNCTION__."(): This action seems to be corrupt.", E_USER_ERROR);
			} elseif($tagsCount == 1) {
				global $wcTagsHolder;
				global $wcComicsHolder;

				$tag = $wcTagsHolder->itemByName($this->target, $this->_group);

				if($tag && $tag->ok()) {
					$comicIds = array();
					foreach($this->_also as $toSearch) {
						//
						// Searching all tags.
						$comicIds = array_merge($comicIds, $this->seeker($toSearch, $tag));
					}
					$comicIds = array_unique($comicIds);

					foreach($comicIds as $id) {
						$comic = $wcComicsHolder->item($id, $this->_group);
						if($comic && $comic->ok()) {
							if(!isset($out["items"])) {
								$out["items"] = array();
							}
							//
							// It seems not to be
							// present, therefore
							// it's added.
							$aux = array(
								"action" => "Adding '{$comic->name}' ({$comic->id}@{$comic->directory}) to tag '{$tag->name}' ({$tag->id})",
								"member" => $comic,
								"status" => true,
								"fatal" => true
							);
							$tag->add($comic);
							$out["items"][] = $aux;
						}

					}
				}
			}
		}

		return $out;
	}
	protected function runModePool() {
		$out = array();

		if($this->_ok) {
			$idMembers = array();
			//
			// Check every loaded tag.
			foreach($this->_tags as $id => $tag) {
				//
				// Select every comic of every tag to be added to
				// the rest of the tags.
				foreach($tag->membersFull() as $comic) {
					//
					// Checking all the rest of tags and if
					// they don't have the comic, adding it.
					foreach($this->_tags as $subId => $subTag) {
						if($id == $subId) {
							// If it's checking over
							// the same tag, it skips
							// it.
							continue;
						}
						//
						// Caching comics ids to gain
						// performance.
						if(!isset($idMembers[$subTag->id])) {
							$idMembers[$subTag->id] = $subTag->members();
						}
						//
						// Checking if the selected comic
						// is in the other tag.
						if(!in_array($comic->id, $idMembers[$subTag->id])) {
							//
							// It seems not to be
							// present, therefore it
							// is added.
							$aux = array(
								"action" => "Adding '{$comic->name}' ($comic->id@$comic->directory) to tag '{$subTag->name}' ({$subTag->id})",
								"member" => $comic,
								"status" => true,
								"fatal" => true
							);
							$subTag->add($comic);
							$out["items"][] = $aux;

							unset($idMembers[$subTag->id]);
						}
					}
				}
			}
		}

		return $out;
	}
	protected function seeker(WCTag &$search, WCTag &$toadd) {
		$out = array();

		if($search->group == $toadd->group) {
			$params = array(
				":group" => $search->group,
				":tag_search" => $search->id,
				":tag_toadd" => $toadd->id
			);
			if($this->_seekerStmt->execute($params)) {
				foreach($this->_seekerStmt->fetchAll() as $row) {
					$out[] = $row["id"];
				}
			}
		}

		return $out;
	}
	//
	// Class Methods
	public static function LockedTagIds(WCGroup &$group) {
		$out = array();

		global $wcDatabaseAccess;

		$db = WCConnection::Instance();
		$dbprefix = $wcDatabaseAccess['prefix'];

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[1]";
		if($db->has($stmtId)) {
			$stmt = $db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$dbprefix}acts_tare\n";

			$stmt = $db->prepare($stmtId, $query);
		}

		if($stmt->execute()) {
			foreach($stmt->fetchAll() as $row) {
				$out[] = $row["act_tag"];
				if($row["act_also"]) {
					$out[] = $row["act_also"];
				}
			}
		}

		return array_unique($out);
	}
	public static function MarkConflicts() {
		global $wcDatabaseAccess;

		$out = 0;

		$db = WCConnection::Instance();
		$dbprefix = $wcDatabaseAccess['prefix'];
		//
		// Looking for tags that move contents to two different
		// directories.
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[POOL-MULTI_TAG]";
		if($db->has($stmtId)) {
			$stmt = $db->get($stmtId);
		} else {
			$query = "update  {$dbprefix}rule_actions\n";
			$query .= "set     rel_ra_conflict = 'Y'\n";
			$query .= "where   (rel_ra_rule,rel_ra_action) in (\n";
			$query .= "                select  act_rule,\n";
			$query .= "                        act_action\n";
			$query .= "                from    {$dbprefix}acts_dor\n";
			$query .= "                where   act_tag in (\n";
			$query .= "                                select  act_tag\n";
			$query .= "                                from    {$dbprefix}acts_dor\n";
			$query .= "                                where   act_mode = 'POOL'\n";
			$query .= "                                group by act_tag\n";
			$query .= "                                having count(distinct(act_action)) > 1))\n";

			$stmt = $db->prepare($stmtId, $query);
		}

		if($stmt->execute()) {
			$out += $stmt->rowCount();
		}

		return $out;
	}
}

// @formatter:off
$wcRuleHooks[TagsRelator::$TYPE] = array_merge($extensionBasicInfo, array(
		"name"		=> "Tags Relator",
		"description"	=> "Enforce relation between tags",

		"class"		=> "TagsRelator",
		"conficts"	=> "TagsRelator::MarkConflicts",
		"lock-tags"	=> "TagsRelator::LockedTagIds",

		//
		// Canvas hooks
		"create"	=> "tare.create",
		"edit"		=> "tare.edit",
		"remove"	=> "tare.remove"
));
// @formatter:on
?>